package fmi.chesstern.game;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


import fmi.chesstern.game.events.GameStateChangedEvent;
import fmi.chesstern.game.events.GameStateChangedListener;
import fmi.chesstern.pieces.ChessPiece;
import fmi.chesstern.pieces.ChessPieceColor;
import fmi.chesstern.pieces.ChessPieceType;
import fmi.events.EventDispatcher;

public class MutableGameBoardState extends GameBoardState implements Cloneable {
	public static final int BOARD_SIZE = 8;
	
	private static final List<BoardField> fields;
	static {
		List<BoardField> fieldsMutable = new ArrayList<BoardField>();
		
		for (int i = 0; i < BOARD_SIZE; i++) {
			for (int j = 0; j < BOARD_SIZE; j++) {
				fieldsMutable.add(new BoardField(i, j));
			}
		}
		
		fields = Collections.unmodifiableList(fieldsMutable);
	}
	
	private ChessPiece[][] board;
	private boolean[][] hasPieceBeenMoved;
	private ChessPieceColor currentPlayer;
	private BoardField whiteKingPosition;
	private BoardField blackKingPosition;
	private EventDispatcher<GameStateChangedEvent, GameStateChangedListener> dispatcher;
	
	public void addGameStateChangedListener(GameStateChangedListener listener) {
		dispatcher.addEventListener(listener);
	}
	
	public void removeGameStateChangedListener(GameStateChangedListener listener) {
		dispatcher.removeEventListener(listener);
	}
	
	public MutableGameBoardState() {
		board = new ChessPiece[BOARD_SIZE][BOARD_SIZE];
		hasPieceBeenMoved = new boolean[BOARD_SIZE][BOARD_SIZE];
		currentPlayer = ChessPieceColor.WHITE;
		dispatcher = new EventDispatcher<GameStateChangedEvent, GameStateChangedListener>();
		
		board[0] = new ChessPiece[] {
			ChessPiece.getPiece(ChessPieceType.ROOK, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.KNIGHT, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.BISHOP, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.QUEEN, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.KING, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.BISHOP, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.KNIGHT, ChessPieceColor.WHITE),
			ChessPiece.getPiece(ChessPieceType.ROOK, ChessPieceColor.WHITE),
		};
		board[BOARD_SIZE - 1] = new ChessPiece[] {
			ChessPiece.getPiece(ChessPieceType.ROOK, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.KNIGHT, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.BISHOP, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.QUEEN, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.KING, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.BISHOP, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.KNIGHT, ChessPieceColor.BLACK),
			ChessPiece.getPiece(ChessPieceType.ROOK, ChessPieceColor.BLACK),
		};
		whiteKingPosition = new BoardField(0, 4);
		blackKingPosition = new BoardField(BOARD_SIZE - 1, 4);
		
		for (int i = 0; i < BOARD_SIZE; i++) {
			board[1][i] = ChessPiece.getPiece(ChessPieceType.PAWN, ChessPieceColor.WHITE);
			board[BOARD_SIZE - 2][i] = ChessPiece.getPiece(ChessPieceType.PAWN, ChessPieceColor.BLACK);
		}
	}
	
	public MutableGameBoardState(GameBoardState state) {
		dispatcher = new EventDispatcher<GameStateChangedEvent, GameStateChangedListener>();
		
		setState(state);
	}

	@Override
	public List<BoardField> getFields() {
		return fields;
	}
	
	@Override
	public ChessPiece getPieceAt(BoardField field) {
		return board[field.getRow()][field.getColumn()];
	}
	
	@Override
	public boolean hasItBeenMoved(BoardField field) {
		return hasPieceBeenMoved[field.getRow()][field.getColumn()];
	}
	
	@Override
	public int getBoardSize() {
		return BOARD_SIZE;
	}
	
	public void makeMove(Move move) {
		int fromRow = move.getFrom().getRow();
		int fromColumn = move.getFrom().getColumn();
		int toRow = move.getTo().getRow();
		int toColumn = move.getTo().getColumn();
		
		if (getPieceAt(move.getFrom()).getType() == ChessPieceType.KING) {
			if (currentPlayer == ChessPieceColor.WHITE) {
				whiteKingPosition = move.getTo();
			} else {
				blackKingPosition = move.getTo();
			}
		}
		board[toRow][toColumn] = board[fromRow][fromColumn];
		board[fromRow][fromColumn] = null;
		hasPieceBeenMoved[fromRow][fromColumn] = hasPieceBeenMoved[toRow][toColumn] = true;
		currentPlayer = currentPlayer.getOpposite();
		
		dispatcher.dispatchEvent(new GameStateChangedEvent(this));
	}
	
	@Override
	public ChessPieceColor getCurrentPlayer() {
		return currentPlayer;
	}
	
	@Override
	public BoardField getKingPositionOf(ChessPieceColor player) {
		return player == ChessPieceColor.WHITE ? whiteKingPosition : blackKingPosition;
	}
	
	private void setState(MutableGameBoardState state) {
		this.currentPlayer = state.currentPlayer;
		this.whiteKingPosition = state.whiteKingPosition;
		this.blackKingPosition = state.blackKingPosition;
		
		board = new ChessPiece[BOARD_SIZE][BOARD_SIZE];
		hasPieceBeenMoved = new boolean[BOARD_SIZE][BOARD_SIZE];
		for (int i = 0; i < BOARD_SIZE; i++) {
			board[i] = state.board[i].clone();
			hasPieceBeenMoved[i] = state.hasPieceBeenMoved[i].clone();
		}
	}
	
	public void setState(GameBoardState state) {
		if (state instanceof MutableGameBoardState) {
			setState((MutableGameBoardState) state);
		} else {
			currentPlayer = state.getCurrentPlayer();
			whiteKingPosition = state.getKingPositionOf(ChessPieceColor.WHITE);
			blackKingPosition = state.getKingPositionOf(ChessPieceColor.BLACK);
			
			board = new ChessPiece[BOARD_SIZE][BOARD_SIZE];
			hasPieceBeenMoved = new boolean[BOARD_SIZE][BOARD_SIZE];
			for (int i = 0; i < BOARD_SIZE; i++) {
				for (int j = 0; j < BOARD_SIZE; j++) {
					board[i][j] = state.getPieceAt(new BoardField(i, j));
					hasPieceBeenMoved[i][j] = state.hasItBeenMoved(new BoardField(i, j));
				}
			}
		}
		
		dispatcher.dispatchEvent(new GameStateChangedEvent(this));
	}
	
	@Override
	public MutableGameBoardState clone() {
		MutableGameBoardState result = null;
		try {
			result = (MutableGameBoardState) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		
		result.board = new ChessPiece[BOARD_SIZE][BOARD_SIZE];
		for (int i = 0; i < BOARD_SIZE; i++) {
			result.board[i] = board[i].clone();
		}
		result.hasPieceBeenMoved = new boolean[BOARD_SIZE][BOARD_SIZE];
		for (int i = 0; i < BOARD_SIZE; i++) {
			result.hasPieceBeenMoved[i] = hasPieceBeenMoved[i].clone();
		}
		
		result.dispatcher = new EventDispatcher<GameStateChangedEvent, GameStateChangedListener>();
		
		return result;
	}
}
